package { 
	import com.greensock.easing.Strong;
	import com.greensock.TweenLite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.events.TimerEvent;
	import flash.geom.Rectangle;
	import flash.media.ID3Info;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.utils.ByteArray;
	import flash.utils.Timer;
	import mediadisplay.Constants;
	import mediadisplay.timeline.contenteditarea.TextArea;
	import mediadisplay.timeline.ContentTypes;
	import mediadisplay.timeline.EditArea;
	import mediadisplay.timeline.TimelineContent;
	import mediadisplay.timeline.TimelineFocusedMarker;
	import mediadisplay.timeline.TimelineOverviewMarker;
	import mediadisplay.ui.colors.ColorMap;
	import mediadisplay.viewerarea.ScreenText;
	import sliderwindow.SliderWindow;
	import mediadisplay.ui.spacecontrols.*;
	
	public class SoundGraph extends Sprite 
	{
		private const WIDTH:int = 1024;
		private const HEIGHT:int = 256;
		
		public var sound:Sound;
		private var samples:ByteArray = new ByteArray();
		private var buffer:BitmapData = new BitmapData(WIDTH,HEIGHT,false,0);	// whole thing
		private var cursorBuffer:BitmapData = new BitmapData(WIDTH,100,true,0);
		private var selectBuffer:BitmapData = new BitmapData(WIDTH,60,true,0);
		private var selectScreen:Bitmap = new Bitmap(selectBuffer);
		private var cursorScreen:Bitmap = new Bitmap(cursorBuffer);
		private var screen:Bitmap = new Bitmap(buffer);
		private var rect:Rectangle;
		private var playingTime:int;
		private var ratio:Number;
		private var step:int;
		private var zone:Rectangle;
		private var selectionRect:Rectangle;
		
		private var clickedPosition:int;
		private var looper:Timer;
		private var channel:SoundChannel;
		
		public var sliderWindow:SliderWindow = new SliderWindow();
		private var sliderWindowRect:Rectangle;
		public var timelineContent:TimelineContent;
		private var totalSeconds:Number;
		private var milliseconds:Number;
		
		private var _waveformData:Array;
		
		private var _url:String;
		public var _main:Main;
		
		private var _waveformArea:Sprite;
		private var _vol:Number = 1;
		private var lastSoundPosition:Number;
		public var _tuneID:int;
		public var isPlaying:Boolean;
		private var lastTriggeredScreenTextIndex:int = -1;
		
		public static const TRACK_LOADED:String = "trackLoaded";
		private var loaderLine:Sprite;
		
		public var tweenTime:Number = 0.4;
		public var startTweenBeforeTarget:Number = 0.4;
		private var editMode:Boolean;
		public var spacecontrol:SpaceControl;
		public var colormap:ColorMap;
		
		public function SoundGraph(main:Main, url:String, tuneID:int):void 
		{
			this._tuneID = tuneID;
			_main = main;
			_url = url;
			
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}		
		
		public function getSound():Sound {
			return _main.getSound();
		}
		
		private function init(e:Event = null):void 
		{
			addEventListener(Event.REMOVED_FROM_STAGE, cleanup);
			
			initSound();
			
			sliderWindowRect = new Rectangle(0, 0, stage.stageWidth - 100, 0);
			sliderWindowRect.y = 33;
			
			addChild( screen );
			addChild( cursorScreen ).y = 0;
			addChild( selectScreen ).y = 34;
			
			_waveformArea = new Sprite;
			addChild(_waveformArea);
			_waveformArea.graphics.beginFill(0xffffff);
			_waveformArea.graphics.drawRect(0, 0, stage.stageWidth, 94);
			_waveformArea.graphics.endFill();
			_waveformArea.alpha = 0.2;
			_waveformArea.blendMode = BlendMode.ADD;
			addChild( sliderWindow );
			
			sliderWindow.y = sliderWindowRect.y;
			sliderWindow.addEventListener(MouseEvent.MOUSE_DOWN, sliderWindowMouseDownHandler);
			
			rect = new Rectangle(0,0,1,0);
			
			zone = new Rectangle(0,0,1,HEIGHT);
			selectionRect = new Rectangle(0,0,0,WIDTH);
			
			looper = new Timer(0, 0);
			
			looper.addEventListener( TimerEvent.TIMER, onTime );
			
			/////
			sound.load(new URLRequest(_url));	
			////
			
			_waveformArea.addEventListener( MouseEvent.CLICK, onClick );
			_waveformArea.addEventListener( MouseEvent.MOUSE_DOWN, onMouseIsDown );
			
			timelineContent = new TimelineContent(this, 0);
			addChild(timelineContent);
			//timelineContent.y = 200;
			
			loaderLine = new Sprite();
			addChild(loaderLine);
			loaderLine.graphics.beginFill(0xffffff);
			loaderLine.graphics.drawRect(0, 0, 1024, 10);
			loaderLine.graphics.endFill();
			loaderLine.y = 100;
			loaderLine.scaleX = 0;
			
			spacecontrol = new SpaceControl(this);
			addChild(spacecontrol);
			spacecontrol.x = 620;
			spacecontrol.y = -80;
			
			var minimize:MinimizeButton = new MinimizeButton();
			addChild(minimize);
			minimize.y = -85;
			minimize.x = 2;
			minimize.addEventListener(MouseEvent.CLICK, minimizeToggle);
			
			colormap = new ColorMap();
			addChild(colormap);
			colormap.x = 1014;
			colormap.y = -80;
			colormap.addEventListener(MouseEvent.MOUSE_OVER, colormapMouseOverHandler);
			colormap.addEventListener(MouseEvent.MOUSE_OUT, colormapMouseOutHandler);
			
		}
		
		private function colormapMouseOutHandler(e:MouseEvent):void 
		{
			//colormap.x = 1014;
			TweenLite.to(colormap, 0.3, { x:1014, ease:Strong.easeIn } );
		}
		
		private function colormapMouseOverHandler(e:MouseEvent):void 
		{
			//colormap.x = 1024 - colormap.width;
			TweenLite.to(colormap, 0.3, { x:1024 - 210, ease:Strong.easeOut } );
		}
		
		private function minimizeToggle(e:MouseEvent):void 
		{
			if (y == 0) {
				y = 255;
				_main.viewerArea.drawMask(550);
			} else {
				y = 0;
				_main.viewerArea.drawMask(300);
			}
		}
		
		private function initSound():void 
		{
			sound = new Sound();
			sound.addEventListener(Event.COMPLETE, loadComplete);
			sound.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			sound.addEventListener(Event.ID3, id3Handler);
			sound.addEventListener(ProgressEvent.PROGRESS, onSoundLoadProgress);
			
		}
		
		private function cleanup(e:Event):void 
		{
			removeEventListener(Event.REMOVED_FROM_STAGE, cleanup);
			stage.removeEventListener( Event.ENTER_FRAME, scrollIt );
			sliderWindow.removeEventListener(MouseEvent.MOUSE_DOWN, sliderWindowMouseDownHandler);
			sound.removeEventListener(Event.COMPLETE, loadComplete);
			sound.removeEventListener(Event.ID3, id3Handler);
			sound.removeEventListener(ProgressEvent.PROGRESS, onSoundLoadProgress);		
			looper.removeEventListener( TimerEvent.TIMER, onTime );
			_waveformArea.removeEventListener( MouseEvent.CLICK, onClick );
			_waveformArea.removeEventListener( MouseEvent.MOUSE_DOWN, onMouseIsDown );			
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			lastTriggeredScreenTextIndex = -1;
			channel.stop();
			
			removeChild(timelineContent);
			removeChild(_waveformArea);
			removeChild(loaderLine);
			removeChild(spacecontrol);
		}
		
		private function onSoundLoadProgress(e:ProgressEvent):void 
		{
			milliseconds = (sound.bytesTotal / (sound.bytesLoaded / sound.length));
			totalSeconds = milliseconds / 1000;
			var Minutes:uint = Math.floor(milliseconds/60000);
			var Seconds:uint = (milliseconds%60000);
			var SecondsTens:uint = Math.floor(Seconds/10000);
			Seconds = Math.ceil(Seconds%10000)
			Seconds /= 1000;
			
			var percentage:Number = e.bytesLoaded / e.bytesTotal;
			//timelineContent.scaleX = percentage;
			loaderLine.scaleX = percentage;
			_main.loaderProgress(percentage);
			//trace("! " + Minutes + ":" + SecondsTens + "" + Seconds);		
		}
		
		private function sliderWindowMouseDownHandler(e:MouseEvent):void 
		{
			sliderWindow.startDrag(false, sliderWindowRect);
			sliderWindow.removeEventListener( MouseEvent.MOUSE_DOWN, sliderWindowMouseDownHandler );
			stage.addEventListener(MouseEvent.MOUSE_UP, sliderWindowMouseUpHandler);
		}
		
		private function sliderWindowMouseUpHandler(e:MouseEvent):void 
		{
			sliderWindow.addEventListener( MouseEvent.MOUSE_DOWN, sliderWindowMouseDownHandler );
			stage.removeEventListener(MouseEvent.MOUSE_UP, sliderWindowMouseUpHandler);
			sliderWindow.stopDrag();
		}
		
		/**
		 * toggle function for edit mode on media in viewerarea
		 */
		public function editToggle():void {
			editMode = !editMode;
			
			for (var i:int = 0; i < _main.viewerArea.objectsOnScreen.length; i++) 
			{
				//var currentObjectIndex:int = timelineContent._currentFocusedMarker.index;
				var currentObjectIndex:int = i;
				var currentObject:ScreenText = ScreenText(_main.viewerArea.objectsOnScreen[currentObjectIndex]);
				
				if (editMode) {
					// turn on edit
					currentObject.editToggle(true);
				} else {
					// turn off edit
					currentObject.editToggle(false);
				}
				
			}
			
		}
		
		private function scrollIt( e:Event ):void
		{
			cursorBuffer.fillRect( cursorBuffer.rect, 0 );
			zone.x = (buffer.width / playingTime) * channel.position;
			cursorBuffer.fillRect( zone , 0xFF990000 );  
			var range:Number = sliderWindow.width;
			var start:Number = sliderWindow.x;			
			var cursorPercentage:Number = (zone.x - start) / range;
			timelineContent.zoomedTimeline.moveCursor(cursorPercentage * stage.stageWidth);				
			
			timelineContent.zoomedTimeline.updateWaveform();
			
			// check timeline for content and display it
			var markerActive:Boolean = false;
			
			// check for loop sections
			for (var j:int = 0; j < timelineContent.loops.length; j++) 
			{
				var loop:Object = timelineContent.loops[j];
				var triggerpoint:Number = loop.triggerpoint;
				var destination:Number = loop.destination;
				if (channel.position / 1000 > triggerpoint && channel.position / 1000 < triggerpoint + 0.2) {
					channel.stop();
					channel = sound.play( destination * 1000 );
					channel.soundTransform = new SoundTransform(_vol);
				}
			}
			
			// check for screen element triggers
			for (var i:int = 0; i < timelineContent.overviewMarkers.length; i++) 
			{
				var marker:TimelineOverviewMarker = timelineContent.overviewMarkers[i];
				if (channel.position / 1000 > marker.start - startTweenBeforeTarget && channel.position / 1000 < marker.stop - startTweenBeforeTarget) {
					_main.setText(marker.content);
					if (lastTriggeredScreenTextIndex != i) {
						if (Constants.CAMERA_AUTO_MOVE_TO_FOCUSED_ELEMENT) {
							_main.viewerArea.moveCamera(i);
						} else {
							// move element seperately
							_main.viewerArea.moveElements(i);
							// move camera seperately
							var newX:Number = timelineContent.keypoints[i].x;
							var newY:Number = timelineContent.keypoints[i].y;
							var newZ:Number = timelineContent.keypoints[i].z;
							_main.viewerArea.moveCameraToPosition(newX, newY, newZ);
						}
						lastTriggeredScreenTextIndex = i;
					}
					
					markerActive = true;
				}
			}
			
			// TODO is this really necessary?  seems only to create havock.
			//if (!markerActive) {
				//_main.setText("");
			//}
		}
		
		private function loadComplete(event:Event):void
		{
			_waveformData = new Array;
			loaderLine.scaleX = 0;
			
			buffer.fillRect( buffer.rect, 0 );
			var extract:Number = Math.floor ((sound.length/1000)*44100);
			playingTime = sound.length;
			ratio = playingTime / buffer.width;
			
			var lng:Number = sound.extract(samples, extract);
			//stage.addEventListener( Event.ENTER_FRAME, scrollIt );
			samples.position = 0;
			step = samples.length / 4096;
			
			do step-- while ( step % 4 );
			
			var left:Number;
			var right:Number;
			
			for (var c:int = 0; c < 4096; c++)
			{
				var percentage:Number = c / 4096;
				timelineContent.zoomedTimeline._waveform.scaleX = percentage;
				
				rect.x = c / 4;
				left = samples.readFloat()*(HEIGHT / 8);
				right = samples.readFloat()*(HEIGHT / 8);
				samples.position = c * step;
				
				//if (left>0)
				if (true)
				{
					rect.height = Math.min((left + right) * 2, 60);
					rect.y = (HEIGHT / 4) - rect.height / 2;
				} else
				{
					rect.y = (HEIGHT / 4);
					rect.height = -left;
				}
				_waveformData.push(rect.height);
				buffer.fillRect( rect, 0xFFFFFF );
				
				//if (right>0)
				//{
					//rect.y = 156 - right;
					//rect.height = right;
				//} else
				//{
					//rect.y = 156;
					//rect.height = -right;
				//}
				//buffer.fillRect( rect, 0xFFFFFF );
			}
			timelineContent.setTrackLength(milliseconds);
			dispatchEvent(new Event(TRACK_LOADED));
		}
		public function addLoop(triggerpoint:Number, destination:Number):void {
			timelineContent.addLoop(triggerpoint, destination);
		}
		public function addKeypoint(from:Number, to:Number, txt:String, rowID:int = 0, contentType:String = ContentTypes.TEXT_CONTENT, xpos:Number = 0, ypos:Number = 0, zpos:Number = 0, rotationX:Number = 0, rotationY:Number = 0, rotationZ:Number = 0, scale:Number = 1, background:String = "", color:uint = 0xffffff, ignore:Boolean = false, interactionTarget:Number = -1):void {
			if (scale == 0) scale = 1;
			timelineContent.addKeypoint(from, to, txt, rowID, contentType, xpos, ypos, zpos, rotationX, rotationY, rotationZ, scale, background, color, ignore, interactionTarget);
		}
		
		public function gotoPosition(pos:Number):void {
			channel.stop();
			channel = sound.play(pos);
			channel.soundTransform = new SoundTransform(_vol);
		}
		
		public function start():void {
			_main.loaderDone();
			
			timelineContent.drawTimeline();
			timelineContent.setWaveformData(_waveformData);
			if (Constants.LOGGED_IN) {
				
				stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
				
			}
			
			channel = sound.play();
			isPlaying = true;
			channel.soundTransform = new SoundTransform(_vol);			
			pause();
			
			stage.addEventListener( Event.ENTER_FRAME, scrollIt );
		}
		private function keyDownHandler(e:KeyboardEvent):void 
		{
			if (TextArea.IN_EDIT_MODE) return;
			if (!Constants.LOGGED_IN) return;
			if (ScreenText.HEY_IM_WORKING_HERE) return;
			
			switch (e.keyCode) {
				case 32:
					// spacebar
					lastSoundPosition = channel.position;
					if (isPlaying) {
						isPlaying = false;
						channel.stop();
					} else {
						resume();
					}
				break;
				case 13:
					stop();
				break;
			}
		}
		public function resume():void {
			if (!sound) {
				initSound();
			}
			isPlaying = true;
			channel = sound.play(lastSoundPosition);
			channel.soundTransform = new SoundTransform(_vol);			
		}
		public function pause():void {
			isPlaying = false;
			lastSoundPosition = channel.position;
			channel.stop();
		}
		public function stop():void {
			timelineContent.zoomedTimeline.removeCursor();
			isPlaying = false;
			lastSoundPosition = 0;
			
			// TODO make the stop function return cursor to beginning
			
			channel.soundTransform = new SoundTransform(_vol);
			channel.stop();
			sound.removeEventListener(Event.COMPLETE, loadComplete);
			sound.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			sound.removeEventListener(Event.ID3, id3Handler);
			sound.removeEventListener(ProgressEvent.PROGRESS, onSoundLoadProgress);
			
		}
		
		public function saveKeypoints():void 
		{
			var loader:URLLoader = new URLLoader();
			var request:URLRequest = new URLRequest("http://www.psychofreud.com/subtitler3000/deletekeypoints.php");
			var data:URLVariables = new URLVariables();			
			data.tune = _tuneID;
			request.data = data;
			request.method = URLRequestMethod.GET;
			
			loader.addEventListener(Event.COMPLETE, keypointsDeleted);
			loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			loader.load(request);
		}
		
		public function hideEditor():void 
		{
			//timelineContent.visible = false;
			//_waveformArea.visible = false;
			//loaderLine.visible = false;
			//sliderWindow.visible = false;
			visible = false;
		}
		
		public function showEditor():void {
			//timelineContent.visible = true;
			//_waveformArea.visible = true;
			//loaderLine.visible = true;
			//sliderWindow.visible = true;
			visible = true;
		}
		
		private function ioErrorHandler(e:IOErrorEvent):void 
		{
			trace("ioError: " + e);
		}
		
		private function keypointsDeleted(e:Event):void 
		{
			// TODO bugs when saving special chars. (')
			var keypoints:Array = timelineContent.keypoints;
			for (var i:int = 0; i < keypoints.length; i++) 
			{
				var keypoint:Object = keypoints[i];
				var start:Number = Number(keypoint.start);
				var stop:Number = Number(keypoint.stop);
				var content:String = String(keypoint.content);
				var row:int = int(keypoint.row);
				var scale:Number = Number(keypoint.scale);
				if (scale == 0) scale = 1;
				var x:Number = Number(keypoint.x);
				var y:Number = Number(keypoint.y);
				var z:Number = Number(keypoint.z);
				var rotationX:Number = Number(keypoint.rotationX);
				var rotationY:Number = Number(keypoint.rotationY);
				var rotationZ:Number = Number(keypoint.rotationZ);
				var background:String = String(keypoint.background);
				var color:uint = uint(keypoint.color);
				var ignore:int = 0;
				if (Boolean(keypoint.ignore) == true) {
					ignore = 1;
				} else {
					ignore = 0;
				}
				
				// TODO dynamic type content
				var contenttype:String = "text";
				
				var loader:URLLoader = new URLLoader();
				var request:URLRequest = new URLRequest("http://www.psychofreud.com/subtitler3000/savekeypoint.php");
				var data:URLVariables = new URLVariables();
				
				data.tune = _tuneID;
				data.start = start;
				data.stop = stop;
				data.content = content;
				data.type = contenttype;
				data.x = x;
				data.y = y;
				data.z = z;
				data.scale = scale;
				data.rotationX = rotationX;
				data.rotationY = rotationY;
				data.rotationZ = rotationZ;
				data.background = background;
				data.color = color;
				data.ignore = ignore;
				trace("sending rotationX: " + rotationX + "  rotationY: " + rotationY + "  rotationZ: " + rotationZ);
				request.data = data;
				request.method = URLRequestMethod.GET;
				loader.addEventListener(Event.COMPLETE, keypointsSaved);
				loader.load(request);
			}
			
		}
		
		private function keypointsSaved(e:Event):void 
		{
			trace(e.target.data);
		}
		private function id3Handler(e:Event):void 
		{
			// TODO id3 handler
		}
		
		private function onTime( e:TimerEvent ):void
		{
			channel.stop();
			channel = sound.play( selectionRect.x * ratio );
			channel.soundTransform = new SoundTransform(_vol);
		}

		private function onMouseIsDown( e:MouseEvent ):void
		{
			stage.addEventListener( MouseEvent.MOUSE_MOVE, onMove );
			selectBuffer.fillRect( selectBuffer.rect, 0 );
			clickedPosition = e.stageX;
			selectionRect.x = clickedPosition;
			selectionRect.width = 0;
		}
		
		private function onMove( e:MouseEvent ):void
		{
			if (e.target is SliderWindow) {
				stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMove );
				return;
			}
			if ( e.stageX > clickedPosition )
			{
				selectionRect.width = e.stageX-clickedPosition;
			} else
			{
				selectionRect.x = e.stageX;
				selectionRect.width = Math.abs (e.stageX-clickedPosition);
			}
			selectBuffer.fillRect( selectionRect, 0x77FF0000 );
		}
		
		private function onClick( e:MouseEvent ):void
		{
			if (e.target is SliderWindow) {
				stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMove );
				return;
			}
			
			channel.stop();
			channel = sound.play( selectionRect.x * ratio );
			channel.soundTransform = new SoundTransform(_vol);
			
			stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMove );
			
			if (selectionRect.width)
			{
				looper.delay = ratio*selectionRect.width;
				looper.start();
			} else looper.stop();
		}

	}
}